using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using org.apache.mina.common;

namespace PDSClient.Utils.IO.Impl
{
    public class CompleteMessageFilter
    {
        private const int DEFAULT_BUFFER_SIZE = 8 * 1024; // should it be only 1024 ? (bit->byte?)
        private const int MAX_BUFFER_SIZE = 512 * 1024;

        private ByteBuffer msgBuf;

        public CompleteMessageFilter()
        {
            msgBuf = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE, false);
        }

        public void FilterReceive(IFilterListener listener, ByteBuffer buf)
        {
            msgBuf.expand(buf.remaining());
            msgBuf.put(buf);
            msgBuf.flip();

            ProcessReceiveBuffer(listener);
        }
        private void ProcessReceiveBuffer(IFilterListener listener)
        {
            if (msgBuf.remaining() > MAX_BUFFER_SIZE)
            {
                /*
                logger.log(Level.WARNING,
                    "Recv filter buffer is larger than expected: {0,number,#}",
                    msgBuf.remaining());
                */
            }

            //Process complete messages, if any
            while (msgBuf.hasRemaining())
            {
                if (msgBuf.remaining() < 4)
                    break;

                if (!msgBuf.prefixedDataAvailable(4))
                    break;

                int msgLen = msgBuf.getInt();

                // Get a read-only buffer view on the complete message
                ByteBuffer completeMessage =
                    msgBuf.slice().asReadOnlyBuffer().limit(msgLen);

                // Advance the underlying message buffer
                msgBuf.skip(msgLen);

                /*
                logger.log(Level.FINER,
                "dispatching complete message of size {0,number,#}",
                msgLen);
                */

                try
                {
                    listener.FilteredMessageReceived(completeMessage);
                }
                catch(Exception e)
                {
                    /*
                    logger.logThrow(Level.WARNING, e,
                        "Exception in message disptach; dropping message");

                    logger.logThrow(Level.FINE, e,
                        "Exception in message disptach; dropping message {0}",
                        completeMessage);
                    */
                    // ignore exception; continue processing the buffer
                }
            }
            msgBuf.compact();
            /*
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST,
                    "partial message {0,number,#} bytes",
                    msgBuf.position());
            }
            */
        }

        public void FilterSend(IFilterListener listener, byte[] message)
        {
            ByteBuffer buffer = ByteBuffer.allocate(message.Length + 4, false);
            buffer.putInt(message.Length);
            buffer.put(message);
            buffer.flip();
            // Don't worry about the listener throwing an exception, since
            // this method has no other side effects.
            listener.SendUnfilteredMessage(buffer);
        }
    }
}
